#include "BDUsuario.h"
#include <algorithm>
#include <sstream>
#include <string>
#include "Log.h"

BDUsuario::BDUsuario() {
    uid_counter = 0;
    estado = BD_SIN_CAMBIO;
}

BDUsuario::~BDUsuario() {
    persistir();
}

int BDUsuario::persistir() {
    if (estado == BD_MODIFICADA) {
        a_usuarios.open(ARCHIVO_USUARIOS, std::fstream::out);
        Json::StyledWriter writer;
        a_usuarios << writer.write(j_usuarios);
        a_usuarios.close();
    }

    return 0;
}

int BDUsuario::cargar() {
    int error = 1;
    a_usuarios.open(ARCHIVO_USUARIOS, std::fstream::in);
    Json::Reader reader;
    std::stringstream ss;
    if (a_usuarios.good()) {
        bool parsingSuccessful = reader.parse(a_usuarios, j_usuarios);
        a_usuarios.close();

        if (!parsingSuccessful) {
            ss << "Error: no se pudo parsear el archivo de usuarios " <<
               ARCHIVO_USUARIOS << ". Error: " <<
               reader.getFormatedErrorMessages() << std::endl;
            Log::loguear(MERROR, ss.str());
        } else {
            uid_counter = j_usuarios["uid_counter"].asInt();
            error = 0;
        }

    } else {
        ss.clear();
        ss << "Advertencia: no se pudo abrir el archivo de usuarios " <<
           ARCHIVO_USUARIOS << std::endl;
        Log::loguear(MWARN, ss.str());
    }

    return error;
}

/** Descripcion: guarda usuario. Si ya existe, lo actualiza. Si no existe, lo agrega
      Retorno:
      -1: error
       0: actualizado
       1: agregado
**/
int BDUsuario::guardar(Usuario &usuario) {
    int error = -1, pos;
    Usuario *usuario_db = NULL;
    Json::Value j_usuario;
    std::string nombre = usuario.getNombre();
    std::transform(nombre.begin(), nombre.end(), nombre.begin(), ::tolower);
    j_usuario["nombre"] = nombre;
    j_usuario["password"] = usuario.getPassword();
    j_usuario["nivel"] = usuario.getNivel();
    if ((pos = buscar(usuario.getNombre(), &usuario_db)) == -1) {
        int uid = ++(this->uid_counter);
        usuario.setUID(uid);
        j_usuario["uid"] = uid;
        j_usuarios["usuarios"].append(j_usuario);
        j_usuarios["uid_counter"] = uid;
        error = 1;
    } else {
        /* Actualiza */
        j_usuario["uid"] = usuario_db->getUID();
        j_usuarios["usuarios"][pos] = j_usuario;
        error = 0;
    }
    if (usuario_db != NULL)
        delete usuario_db;
    if (error != -1)
        setEstado(BD_MODIFICADA);

    return error;
}

void BDUsuario::setEstado(int estado) {
    this->estado = estado;
}

void BDUsuario::imprimir() {
    std::cout << this->j_usuarios;
}

std::string BDUsuario::aString(){
    std::stringstream ss;
    ss << j_usuarios;
return ss.str();
}

void BDUsuario::reiniciar() {
    if (remove(ARCHIVO_USUARIOS) == 0)
        Log::loguear(MDEBUG, "Base de datos de usuarios reiniciada.");
}

/**
  * Descripcion: si no se encontro retorna -1 y NULL como usuario
  * en caso contrario la posicion y una instancia del usuario.
  *
  * IMPORTANTE: pasarle NULL como usuario si no es necesario.
  */
int BDUsuario::buscar(std::string nombre, Usuario **usuario) {
    bool encontrado = false;
    int index = 0, nivel, uid;
    std::string name, password;
    std::transform(nombre.begin(), nombre.end(), nombre.begin(), ::tolower);
    while (!encontrado && index < (int)j_usuarios["usuarios"].size())  {
        if (j_usuarios["usuarios"][index]["nombre"] == nombre) {
            if (usuario != NULL) {
                uid= j_usuarios["usuarios"][index]["uid"].asInt();
                name= j_usuarios["usuarios"][index]["nombre"].asString();
                password= j_usuarios["usuarios"][index]["password"].asString();
                nivel= j_usuarios["usuarios"][index]["nivel"].asInt();
                (*usuario) = new Usuario();
                (*usuario)->setUID(uid);
                (*usuario)->setNombre(name);
                (*usuario)->setPassword(password);
                (*usuario)->setNivel(nivel);
            }
            encontrado = true;
        } else {
            index++;
        }
    }
    if (!encontrado)
        return -1;
    return index;
}


